package  
{
	import org.flixel.*;
	/**
	 * ...
	 * @author morgan
	 */
	public class Bug extends FlxSprite
	{
		[Embed (source = "../data/bug2.png")] private var bugImg:Class;
		
		public var bugGroup:FlxGroup;
		public var direction:Number = 0;
		
		public var flockRadius:Number = 40;
		public var flockFrame:int = 0;
		
		//flock params
		public var sepMinDistance:Number = 20;
		public var sepForce:Number = 50;
		public var sepColDistance:Number = 10;
		public var sepColForce:Number = 100;
		
		public var cohesionForce:Number = 150;
		
		public var cursorForce:Number = 180;
		
		public var friction:Number = 0.02;
		
		public var turnRate:Number = 3.14; // rad/sec
		public var speed:Number = 550; // pix/sec
		public var maxspeed:Number = 550; // pix/sec
		
		// bizarre stuff
		public var z:Number = 0;
		public var lastValid:FlxPoint = new FlxPoint(0, 0);
		
		public function Bug(x:int, y:int, bugGroup:FlxGroup) 
		{
			super(x, y);
			loadGraphic(bugImg, true, false, 4, 4);
			frame = 0;
			this.bugGroup = bugGroup;
			z = Math.random() * 6;
			flockFrame = Math.random() * 4;
			lastValid = new FlxPoint(x, y);
			color = 0xff000000;
		}
		
		public function calcSeparation(other:Bug, d:Number):FlxPoint
		{
			var dist:Number;
			if (d <= sepMinDistance * sepMinDistance)
			{
				dist = Math.sqrt(d);
				return new FlxPoint((x - other.x) / dist * sepForce, (y - other.y) / dist * sepForce);
			}
			return new FlxPoint(0, 0);
		}
		
		public function accelTowards(p:FlxPoint, f:Number):void
		{
			var d:Number = Math.max(1, Math.sqrt((p.x - x) * (p.x - x) + (p.y - y) * (p.y -y)));
			acceleration.x += (p.x - x) / d * f;
			acceleration.y += (p.y - y) / d * f;
		}
		
		public function flock():void
		{
			var state:GameState = (FlxG.state as GameState);
			
			// Does the flocking behaviors separation & cohesion
			// Instead of alignment, they just all go towards the cursor.
			var d:Number = 0;
			var numBugs:Number = 0;
			
			acceleration = new FlxPoint(0, 0);
			
			// Cohesion requires the avg position of all bugs
			var avgPos:FlxPoint = new FlxPoint(0, 0);
			
			for each(var b:Bug in bugGroup.members)
			{
				if (b == this) { continue; }
				d = (b.x - x) * (b.x - x) + (b.y - y) * (b.y - y);
				d = Math.max(1, d);
				// Only do stuff if they're in flock radius.
				if (d < flockRadius * flockRadius)
				{
					var force:FlxPoint = calcSeparation(b, d);
					avgPos.x += b.x;
					avgPos.y += b.y;
					acceleration.x += force.x;
					acceleration.y += force.y;
					numBugs += 1;
				}
				// Only pay attention to a certain number of nearby bugs.
				// Efficiency reasons, mostly, but also technicaly accurate for flocks of things.
				if (numBugs >= 4)
				{
					break;
				}
			}
			if (numBugs > 0)
			{
				avgPos.x /= numBugs;
				avgPos.y /= numBugs;
				// Accel towards the avg position.
				accelTowards(avgPos, cohesionForce);
			}
			
			// Accel towards the cursor.
			accelTowards(new FlxPoint(state.bugTarget.x, state.bugTarget.y), cursorForce);
			
			// Clamp max speed
			d = Math.max((velocity.x * velocity.x) + (velocity.y * velocity.y), 1);
			if (d > speed * speed)
			{
				var dist:Number = Math.sqrt(d);
				velocity.x = velocity.x / dist * speed;
				velocity.y = velocity.y / dist * speed;
			}
			
		}
		
		public function updateJustSpawned():void
		{
			if (color != 0xffffffff)
			{
				var a:uint = 255;
				var r:uint = (color >> 16) % 256;
				var g:uint = (color >> 8) % 256;
				var b:uint = (color >> 0) % 256;
				
				r = Math.max(r - 1, 0);
				g = Math.max(g - 1, 0);
				b = Math.max(b - 1, 0);
				
				color = (a << 24) + (r << 16) + (g << 8) + (b << 0);
			}
			
			if (speed < maxspeed)
			{
				speed = Math.min(speed + FlxG.elapsed * 100, maxspeed);
			}
		}

		override public function update():void 
		{
			updateJustSpawned();
			
			var tempFrame:int;
			// Figure out which frame to set, based on velocity
			direction = Math.atan2(velocity.y, velocity.x) + 3.14;
			if (direction <= 0) { direction += 6.2818; }
			direction = direction % 6.2818;
			tempFrame = int(direction * 8 / 6.2818 + (6.2818 / 16.0)) % 8;
			
			// Test
			if (FlxG.mouse.pressed())
			{
				cursorForce = Math.min(cursorForce + FlxG.elapsed * 200, 350);
				cohesionForce = Math.min(cohesionForce + FlxG.elapsed * 200, 250);
				sepForce = Math.max(sepForce - FlxG.elapsed * 100, 5);
				friction = 0.03;
			}
			else
			{
				cursorForce = Math.max(cursorForce - FlxG.elapsed * 200, 180);
				cohesionForce = Math.max(cohesionForce - FlxG.elapsed * 200, 150);
				sepForce = Math.min(sepForce + FlxG.elapsed * 100, 150);
				friction = 0.03;
			}
			
			// Do flocking behavior junk
			if (flockFrame < 0)
			{
				flock();
				flockFrame = 3;
			}
			else
			{
				flockFrame -= 1;
			}
			// Apply friction
			velocity.x *= (1.0 - friction);
			velocity.y *= (1.0 - friction);		
			
			// Make it look like the bugs have some depth
			// frame 8 is a tiny bug, 9 is a big bug. 
			z += FlxG.elapsed * 2;
			if (Math.sin(z) < 0.25)
			{
				tempFrame = 8;
			}
			var d:Number = Math.max((velocity.x * velocity.x) + (velocity.y * velocity.y), 1);
			if (d <= 15 * 15 && Math.sin(z) > 0.5)
			{
				tempFrame = 9;
			}
			if (Math.sin(z * 3) < 0)
			{
				frame = tempFrame + 10
			}
			else
			{
				frame = tempFrame;
			}
			
			super.update();
		}
		
	}

}